#include "bsp_dma.h"

static const uint32_t dma_width_mem[3] = {DMA_MEMORY_WIDTH_8BIT, DMA_MEMORY_WIDTH_16BIT, DMA_MEMORY_WIDTH_32BIT};
static const uint32_t dma_width_per[3] = {DMA_PERIPHERAL_WIDTH_8BIT, DMA_PERIPHERAL_WIDTH_16BIT, DMA_PERIPHERAL_WIDTH_32BIT};

DMA_DEF(DMA_MEM, DMA1, DMA_CH2, Circulation_Disable);

/**
 * 初始化特定设备的DMA通信。
 * 
 * 该函数根据指定的参数配置DMA控制器，以实现外设和内存之间的数据传输。
 * 包括设置传输方向、数据宽度以及是否启用循环模式。
 * 
 * @param dmax 指向DMA控制器结构的指针。
 * @param periph_addr 外设地址，用于数据传输。
 * @param memory_addr 内存地址，用于数据传输。
 * @param dma_width DMA传输的数据宽度。
 * @param dma_direction DMA传输的方向。
 */
void driver_dma_com_init(typdef_dma_general* dmax, uint32_t periph_addr, uint32_t memory_addr, dma_width_enum dma_width, uint32_t dma_direction) {
    dma_parameter_struct dma_init_struct;

    /* 使能DMA时钟 */
    rcu_periph_clock_enable(dmax->rcu_dmax);

    /* 反初始化DMA通道 */
    dma_deinit(dmax->dmax, dmax->dma_chx);

    /* 初始化DMA结构参数 */
    dma_struct_para_init(&dma_init_struct);
    /* 配置DMA传输方向 */
    dma_init_struct.direction = dma_direction;
    /* 启用内存地址递增 */
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    /* 配置内存数据宽度 */
    dma_init_struct.memory_width = dma_width_mem[dma_width];
    /* 设置数据传输次数为0，表示无限制 */
    dma_init_struct.number = 0;
    /* 配置外设地址 */
    dma_init_struct.periph_addr = periph_addr;
    /* 禁用外设地址递增 */
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    /* 配置外设数据宽度 */
    dma_init_struct.periph_width = dma_width_per[dma_width];
    /* 设置DMA传输优先级为高 */
    dma_init_struct.priority = DMA_PRIORITY_HIGH;
    /* 使用配置的参数初始化DMA通道 */
    dma_init(dmax->dmax, dmax->dma_chx, &dma_init_struct);

    /* 根据指定参数配置DMA循环模式 */
    if (dmax->circulation == Circulation_Disable) {
        /* 禁用DMA循环模式 */
        dma_circulation_disable(dmax->dmax, dmax->dma_chx);
    } else {
        /* 启用DMA循环模式 */
        dma_circulation_enable(dmax->dmax, dmax->dma_chx);
    }

    /* 根据回调函数是否存在，使能DMA传输完成中断 */
    if (dmax->Complete_callback != NULL) {
        /* 使能DMA传输完成中断，用于触发DMA传输 */
        dma_interrupt_enable(dmax->dmax, dmax->dma_chx, DMA_CHXCTL_FTFIE);	
    } else {
        /* 使能DMA传输完成中断 */
        dma_interrupt_enable(dmax->dmax, dmax->dma_chx, DMA_CHXCTL_FTFIE);		
    }			
}


/**
 * @brief 初始化DMA内存传输驱动
 * 
 * 该函数用于初始化DMA控制器的内存传输功能，使其能够进行外设到内存的数据传输
 * 主要配置了DMA的数据宽度为8位，以及传输方向为外设到内存
 * 
 * @param dmax DMA控制器的实例指针，用于指定要初始化的DMA控制器
 */
void driver_dma_mem_init(typdef_dma_general* dmax) {
    // 初始化DMA控制器，配置数据宽度为8位，传输方向为外设到内存
    driver_dma_com_init(dmax, NULL, NULL, DMA_Width_8BIT, DMA_PERIPHERAL_TO_MEMORY);
}

/**
 * @brief 启动DMA内存到内存的传输
 * 
 * 本函数配置并启动DMA控制器进行内存到内存的数据传输。它根据提供的参数配置DMA控制器的传输方向、数据宽度、传输长度等。
 * 
 * @param memory_dest_addr 目标内存地址
 * @param memory_src_addr 源内存地址
 * @param dma_width 数据宽度枚举
 * @param length 传输长度，以数据宽度为单位
 */
void driver_dma_mem_to_mem_start(void* memory_dest_addr, void* memory_src_addr, dma_width_enum dma_width, uint32_t length) {
    // 初始化传输计数器
    __IO uint32_t count = 0;
    // 定义不同数据宽度对应的字节数
    const uint8_t size[3] = {1, 2, 4};

    // 禁用DMA通道以进行配置
    dma_channel_disable(DMA_MEM.dmax, DMA_MEM.dma_chx);

    // 配置DMA传输方向为内存到外设
    dma_transfer_direction_config(DMA_MEM.dmax, DMA_MEM.dma_chx, DMA_MEMORY_TO_PERIPHERAL);

    // 配置DMA内存和外设的数据宽度
    dma_memory_width_config(DMA_MEM.dmax, DMA_MEM.dma_chx, dma_width_mem[dma_width]);
    dma_periph_width_config(DMA_MEM.dmax, DMA_MEM.dma_chx, dma_width_per[dma_width]);

    // 启用内存和外设地址的递增模式
    dma_periph_increase_enable(DMA_MEM.dmax, DMA_MEM.dma_chx);
    dma_memory_increase_enable(DMA_MEM.dmax, DMA_MEM.dma_chx);

    // 配置DMA通道优先级为低
    dma_priority_config(DMA_MEM.dmax, DMA_MEM.dma_chx, DMA_PRIORITY_LOW);

    // 禁用DMA循环传输模式
    dma_circulation_disable(DMA_MEM.dmax, DMA_MEM.dma_chx);

    // 启用DMA内存到内存的传输模式
    dma_memory_to_memory_enable(DMA_MEM.dmax, DMA_MEM.dma_chx);

    // 分批次传输数据，每次最多传输0xffff个数据单元
    do {
        if (count != 0) {
            // 如果不是第一批传输，先禁用DMA通道进行重新配置
            dma_channel_disable(DMA_MEM.dmax, DMA_MEM.dma_chx);
        }

        // 配置本次传输的目标和源地址
        dma_periph_address_config(DMA_MEM.dmax, DMA_MEM.dma_chx, ((uint32_t)memory_dest_addr) + 0xffff * size[dma_width]*count);
        dma_memory_address_config(DMA_MEM.dmax, DMA_MEM.dma_chx, ((uint32_t)memory_src_addr) + 0xffff * size[dma_width]*count);

        // 更新传输批次计数器
        count++;
        if (length > 0xffff) {
            // 如果剩余传输长度超过0xffff，本次传输0xffff个数据单元
            dma_transfer_number_config(DMA_MEM.dmax, DMA_MEM.dma_chx, 0xffff);
            length -= 0xffff;
        } else {
            // 如果剩余传输长度不超过0xffff，本次传输剩余的所有数据单元
            dma_transfer_number_config(DMA_MEM.dmax, DMA_MEM.dma_chx, length);
            length = 0;
        }
        // 启用DMA通道开始传输
        dma_channel_enable(DMA_MEM.dmax, DMA_MEM.dma_chx);
        // 等待本次传输完成
//        driver_dma_flag_wait_timeout(&DMA_MEM, DMA_FLAG_FTF, SET);
    } while (length);
}

/**
 * @brief 启动从内存到EXMC LCD的DMA传输
 * 
 * 该函数配置并启动DMA传输，将数据从指定的内存地址传输到EXMC LCD控制器。
 * 它支持多次DMA传输，直到所有数据都被传输完毕。
 * 
 * @param memory_dest_addr 目标内存地址，即EXMC LCD控制器的地址
 * @param memory_src_addr 源内存地址，即要传输的数据在内存中的起始地址
 * @param dma_width DMA传输宽度，决定了每次传输的数据大小
 * @param length 要传输的数据长度，以传输宽度为单位
 */
void driver_dma_mem_to_exmclcd_start(void* memory_dest_addr, void* memory_src_addr, dma_width_enum dma_width, uint32_t length) {
    __IO uint32_t count = 0;
    // 定义传输宽度对应的字节数
    const uint8_t size[3] = {1, 2, 4};

    // 禁用DMA通道，准备配置
    dma_channel_disable(DMA_MEM.dmax, DMA_MEM.dma_chx);

    // 配置DMA传输方向为从内存到外设
    dma_transfer_direction_config(DMA_MEM.dmax, DMA_MEM.dma_chx, DMA_MEMORY_TO_PERIPHERAL);

    // 配置DMA内存和外设的传输宽度
    dma_memory_width_config(DMA_MEM.dmax, DMA_MEM.dma_chx, dma_width_mem[dma_width]);
    dma_periph_width_config(DMA_MEM.dmax, DMA_MEM.dma_chx, dma_width_per[dma_width]);

    // 启用内存地址递增模式
    dma_memory_increase_enable(DMA_MEM.dmax, DMA_MEM.dma_chx);

    // 禁用外设地址递增模式
    dma_periph_increase_disable(DMA_MEM.dmax, DMA_MEM.dma_chx);

    // 配置DMA通道优先级为低
    dma_priority_config(DMA_MEM.dmax, DMA_MEM.dma_chx, DMA_PRIORITY_LOW);

    // 禁用DMA循环传输模式
    dma_circulation_disable(DMA_MEM.dmax, DMA_MEM.dma_chx);

    // 启用内存到内存的传输模式，尽管这里用于内存到外设的传输
    dma_memory_to_memory_enable(DMA_MEM.dmax, DMA_MEM.dma_chx);

    // 主循环，负责分块传输数据
    do {
        if (count != 0) {
            // 如果不是第一次传输，先禁用DMA通道进行重新配置
            dma_channel_disable(DMA_MEM.dmax, DMA_MEM.dma_chx);
        }

        // 配置DMA外设地址（目标地址）和内存地址（源地址）
        dma_periph_address_config(DMA_MEM.dmax, DMA_MEM.dma_chx, ((uint32_t)memory_dest_addr));
        dma_memory_address_config(DMA_MEM.dmax, DMA_MEM.dma_chx, ((uint32_t)memory_src_addr) + 0xffff * size[dma_width]*count);

        count++;
        // 根据剩余数据长度配置传输数量
        if (length > 0xffff) {
            dma_transfer_number_config(DMA_MEM.dmax, DMA_MEM.dma_chx, 0xffff);
            length -= 0xffff;
        } else {
            dma_transfer_number_config(DMA_MEM.dmax, DMA_MEM.dma_chx, length);
            length = 0;
        }

        // 启用DMA通道开始传输
        dma_channel_enable(DMA_MEM.dmax, DMA_MEM.dma_chx);

        // 等待DMA传输完成
 //       driver_dma_flag_wait_timeout(&DMA_MEM, DMA_FLAG_FTF, SET);
    } while (length);
}

/**
 * @brief 启动DMA传输
 * 
 * 本函数负责配置和启动DMA通道进行数据传输。它首先禁用DMA通道，然后配置DMA的内存地址和传输数量，最后启用DMA通道开始传输。
 * 
 * @param dmax DMA控制器的配置结构体指针，包含DMA控制器的配置信息
 * @param pbuff 指向要传输的数据缓冲区的指针
 * @param length 要传输的数据长度，以字节为单位
 */
void driver_dma_start(typdef_dma_general* dmax, uint8_t* pbuff, uint16_t length) {
    // 禁用DMA通道，准备进行配置
	dma_channel_disable(dmax->dmax, dmax->dma_chx);
	
    // 配置DMA通道的内存地址为要传输的数据缓冲区地址
	dma_memory_address_config(dmax->dmax, dmax->dma_chx, (uint32_t)pbuff);
	
    // 配置DMA通道的数据传输数量
	dma_transfer_number_config(dmax->dmax, dmax->dma_chx, length);
	
    // 启用DMA通道，开始数据传输
	dma_channel_enable(dmax->dmax, dmax->dma_chx);
}

/**
 * @brief 等待DMA标志达到预期状态或超时
 *
 * 此函数用于等待特定DMA通道的标志位达到预期状态，或者直到超时时间结束。
 * 它主要用于同步DMA数据传输的完成或其他重要事件。
 *
 * @param dmax DMA设备结构体指针，包含DMA设备的相关信息。
 * @param flag 需要检查的DMA标志位。
 * @param wait_state 期望的标志位状态，函数将等待标志位达到此状态。
 *
 * @return Drv_Err 返回操作结果，DRV_SUCCESS表示成功，DRV_ERROR表示超时。
 */
//Drv_Err driver_dma_flag_wait_timeout(typdef_dma_general* dmax, uint32_t flag, FlagStatus wait_state) {
//	// 初始化超时时间为当前计时器值
//	uint64_t timeout = driver_tick;

//	// 检查DMA通道是否已启用
//	if ((DMA_CHCTL(dmax->dmax, dmax->dma_chx)&DMA_CHXCTL_CHEN) == DMA_CHXCTL_CHEN) {
//		// 等待标志位达到预期状态或超时
//		while (wait_state != dma_flag_get(dmax->dmax, dmax->dma_chx, flag)) {
//			// 检查是否已超时
//			if ((timeout + DMA_TIMEOUT_MS) <= driver_tick) {
//				return DRV_ERROR;
//			}
//		}
//		// 清除DMA标志位
//		dma_flag_clear(dmax->dmax, dmax->dma_chx, flag);
//	}
//	// 返回操作结果：成功
//	return DRV_SUCCESS;
//}

